#pragma once

#include "vec3.h"
#include "../arch/avx.h"

OPEN_JLIB_NS


struct Box3 {

  f32v4 min, max;

  inline Box3 () noexcept = default;
  inline Box3 (f32v4 min, f32v4 max) noexcept : min(min), max(max) {
  }
  inline Box3 (f32 minx, f32 miny, f32 minz, f32 maxx, f32 maxy, f32 maxz) noexcept {
    min = avx_vunpcklps<f32v4>(minx, miny);
    max = avx_vunpcklps<f32v4>(maxx, maxy);
    min = avx_vinsertps<0x28, f32v4>(min, minz);
    max = avx_vinsertps<0x28, f32v4>(max, maxz);
  }
  inline Box3 (Vec3 min, Vec3 max) noexcept : min(min.p), max(max.p) {
  }
  inline Box3 (const f32 *m) noexcept {
    min = avx_vlddqu<f32v4>(m);
    max = avx_vlddqu<f32v4>(m+4);
  }


  inline Box3& operator&= (Box3 v) noexcept {
    min = avx_vmaxps<f32v4>(min, v.min);
    max = avx_vminps<f32v4>(max, v.max);
    return *this;
  }
  inline Box3& operator|= (Box3 v) noexcept {
    min = avx_vminps<f32v4>(min, v.min);
    max = avx_vmaxps<f32v4>(max, v.max);
    return *this;
  }


  inline Box3 operator& (Box3 v) const noexcept {
    auto a = avx_vmaxps<f32v4>(min, v.min);
    auto b = avx_vminps<f32v4>(max, v.max);
    return {a, b};
  }
  inline Box3 operator| (Box3 v) const noexcept {
    auto a = avx_vminps<f32v4>(min, v.min);
    auto b = avx_vmaxps<f32v4>(max, v.max);
    return {a, b};
  }


  inline Vec3 operator~ () const noexcept {
    return max - min;
  }


  inline f32 x () const noexcept {
    return min[0];
  }
  inline f32 y () const noexcept {
    return min[1];
  }
  inline f32 z () const noexcept {
    return min[2];
  }
  inline f32 X () const noexcept {
    return max[0];
  }
  inline f32 Y () const noexcept {
    return max[1];
  }
  inline f32 Z () const noexcept {
    return max[2];
  }


  inline Vec3 xyz () const noexcept {
    return min;
  }
  inline Vec3 xyZ () const noexcept {
    return avx_vblendps<0x4, f32v4>(min, max);
  }
  inline Vec3 xYz () const noexcept {
    return avx_vblendps<0x2, f32v4>(min, max);
  }
  inline Vec3 xYZ () const noexcept {
    return avx_vblendps<0x6, f32v4>(min, max);
  }
  inline Vec3 Xyz () const noexcept {
    return avx_vblendps<0x1, f32v4>(min, max);
  }
  inline Vec3 XyZ () const noexcept {
    return avx_vblendps<0x5, f32v4>(min, max);
  }
  inline Vec3 XYz () const noexcept {
    return avx_vblendps<0x3, f32v4>(min, max);
  }
  inline Vec3 XYZ () const noexcept {
    return max;
  }


  inline bool contains (Vec3 v) const noexcept {
    auto a0 = avx_vcmpps<avx::NLE_UQ, f32v4>(min, v.p);
    auto a1 = avx_vcmpps<avx::NLE_UQ, f32v4>(v.p, max);
    auto z0 = avx_vinsertps<0xF8, f32v4>(a0, a0);
    auto z1 = avx_vinsertps<0xF8, f32v4>(a1, a1);
    auto m0 = avx_vmovmskps_<u32>(z0);
    auto m1 = avx_vmovmskps_<u32>(z1);
    return (m0|m1) == 0;
  }
  inline bool contains (Box3 v) const noexcept {
    auto a0 = avx_vcmpps<avx::NLE_UQ, f32v4>(min, v.min);
    auto a1 = avx_vcmpps<avx::NLE_UQ, f32v4>(v.max, max);
    auto z0 = avx_vinsertps<0xF8, f32v4>(a0, a0);
    auto z1 = avx_vinsertps<0xF8, f32v4>(a1, a1);
    auto m0 = avx_vmovmskps_<u32>(z0);
    auto m1 = avx_vmovmskps_<u32>(z1);
    return (m0|m1) == 0;
  }
  inline bool intersect (Box3 v) const noexcept {
    auto a0 = avx_vcmpps<avx::NLT_UQ, f32v4>(min, v.max);
    auto a1 = avx_vcmpps<avx::NLT_UQ, f32v4>(v.min, max);
    auto z0 = avx_vinsertps<0xF8, f32v4>(a0, a0);
    auto z1 = avx_vinsertps<0xF8, f32v4>(a1, a1);
    auto m0 = avx_vmovmskps_<u32>(z0);
    auto m1 = avx_vmovmskps_<u32>(z1);
    return (m0|m1) == 0;
  }
};


CLOSE_JLIB_NS